package com.meshnetics.gb.stack.mac.primitives;

import com.meshnetics.gb.stack.core.IBParameter;
import com.meshnetics.gb.stack.core.event.StackConfirm;
import com.meshnetics.gb.stack.core.event.StackEvent;
import com.meshnetics.gb.stack.core.event.StackEventType;
import com.meshnetics.gb.stack.core.layer.StackLayer;
import com.meshnetics.gb.stack.mac.RadioMACStackLayer;
import com.meshnetics.gb.stack.mac.type.MACStatus;
import com.meshnetics.gb.stack.mac.type.TypeMACStatus;
import com.meshnetics.gb.type.DataType;
import com.meshnetics.gb.type.DataValue;
import com.meshnetics.gb.type.impl.ByteUtils;
import com.meshnetics.gb.type.impl.zcl.Bool;
import com.meshnetics.gb.type.impl.zcl.TypeStr;
import com.meshnetics.gb.type.impl.zcl.TypeUInt;
import com.meshnetics.gb.type.impl.zcl.UInt;

import java.nio.ByteBuffer;

/**
 * WRITE DESCRIPTION HERE
 *
 * @author AKovylin
 */
public class MACGetConfirm extends StackConfirm implements MACReplyOnCommand {
    public static final byte CODE = 0x4e;

    private static final byte[] ALLOWED_STATUSES = new byte[]{
            MACStatus.SUCCESS.getCode(),
            MACStatus.UNSUPPORTED_ATTRIBUTE.getCode()
    };

    private final UInt pibAttribute;
    private final Object pibAttributeValue;

    public MACGetConfirm(ByteBuffer bb, RadioMACStackLayer layer) {
        super("macGetConfirm", StackEventType.LME_TYPE, null, null, layer);
        ByteUtils.ensureNotNull(bb);
        ByteUtils.ensureCapacity(bb, 2);
        MACStatus status = (MACStatus) TypeMACStatus.TYPE.deserialize(bb);
        Utils.checkStatus(status, ALLOWED_STATUSES);
        setStatus(status);
        this.pibAttribute = (UInt) TypeUInt.UINT_8.deserialize(bb);
        IBParameter ibp = layer.getInformationBase().getParameter(((Number) pibAttribute.toObject()).intValue());
        if (ibp == null) {
            throw new IllegalArgumentException("unknown parameter " + pibAttribute);
        }
        DataType type = ibp.getType();
        if (type instanceof TypeStr) {
            pibAttributeValue = ((TypeStr) type).deserialize(bb, bb.remaining(), false);
        } else {
            pibAttributeValue = type.deserialize(bb);
            if (pibAttributeValue instanceof Bool && ((Bool) pibAttributeValue).isInvalid()) {
                throw new IllegalArgumentException("invalid pibAttributeValue");
            }
        }
        if (bb.remaining() > 0) {
            throw new IllegalArgumentException("unexpected content of ByteBuffer, " + bb.remaining() + " bytes left");
        }
    }

    public MACGetConfirm(MACStatus status, int pibAttribute, Object pibAttributeValue) {
        this(status, (byte) (pibAttribute & 0xff), pibAttributeValue);
    }

    public MACGetConfirm(MACStatus status, byte pibAttribute, Object pibAttributeValue) {
        this(status, new Byte(pibAttribute), pibAttributeValue);
    }

    public MACGetConfirm(MACStatus status, Byte pibAttribute, Object pibAttributeValue) {
        super("macGetConfirm", StackEventType.LME_TYPE, null);
        if (status == null) {
            throw new NullPointerException("status is null");
        }
        Utils.checkStatus(status, ALLOWED_STATUSES);
        if (pibAttributeValue == null) {
            throw new NullPointerException("pibAttributeValue is null");
        }
        setStatus(status);
        this.pibAttribute = (UInt) TypeUInt.UINT_8.fromObject(pibAttribute);
        this.pibAttributeValue = pibAttributeValue;
    }

    public MACGetConfirm(MACStatus status, UInt pibAttribute, DataValue pibAttributeValue) {
        this(status, pibAttribute, pibAttributeValue, null);
    }

    public MACGetConfirm(MACStatus status, UInt pibAttribute, DataValue pibAttributeValue, StackLayer source) {
        super("macGetConfirm", StackEventType.LME_TYPE, null, null, source);
        if (status == null) {
            throw new NullPointerException("status is null");
        }
        Utils.checkStatus(status, ALLOWED_STATUSES);
        if (pibAttribute == null) {
            throw new NullPointerException("pibAttribute is null");
        }
        if (pibAttributeValue == null) {
            throw new NullPointerException("pibAttributeValue is null");
        }
        if (pibAttributeValue instanceof Bool && ((Bool) pibAttributeValue).isInvalid()) {
            throw new IllegalArgumentException("invalid pibAttributeValue");
        }
        setStatus(status);
        this.pibAttribute = pibAttribute.convert(TypeUInt.UINT_8);
        this.pibAttributeValue = pibAttributeValue;
    }

    public UInt getPibAttributeType() {
        return pibAttribute;
    }

    public Byte getPibAttribute() {
        return (Byte) pibAttribute.toObject();
    }

    /**
     * Returns value of attribute or null is value was set in form of Java object.
     *
     * @return value of attribute or null is value was set in form of Java object
     */
    public DataValue getPibAttributeValueType() {
        return pibAttributeValue instanceof DataValue ? (DataValue) pibAttributeValue : null;
    }

    /**
     * Returns value of attribute in form of Java object.
     *
     * @return value of attribute in form of Java object
     */
    public Object getPibAttributeValue() {
        return pibAttributeValue instanceof DataValue ? ((DataValue) pibAttributeValue).toObject() : pibAttributeValue;
    }

    public boolean isReplyOn(StackEvent se) {
        return se instanceof MACGetRequest && pibAttribute.equals(((MACGetRequest) se).getPibAttributeType());
    }

    public String toString() {
        return getName() + '(' + getStatus() + ',' + pibAttribute + ',' + pibAttributeValue + ')';
    }
}
